package com.linxcong.security.server.auth;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;

import javax.sql.DataSource;

/**
 * @Author:linchong
 * @CreateTime:2020-07-14 11:59
 * @Description:认证授权
 */
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private AuthenticationManager authenticationManager;

	/**
	 * 认证服务器的职责：
	 *  知晓oauth2协议中其他涉及的角色以及特征
	 *  如有哪些客户端应用可以向我来请求，有哪些合法的用户，发出的令牌应该用来访问那些资源服务器
	 *  三个信息对应着三个配置
	 */


	/**
	 * 数据库存储-1
	 */
	@Autowired
	private DataSource dataSource;

	/**
	 * 数据库存储-2
	 * token信息放入到数据库中
	 * tokenStore:存取token,默认是内存中的实现，现使用jdbc替代它
	 */
	@Bean
	public TokenStore tokenStore(){
		return new JdbcTokenStore(dataSource);
	}


	/**
	 * 1.客户端应用，让认证服务器知道，有哪些客户端应用会来找它来要令牌
	 * @param clients 客户端应用的详情信息
	 * @throws Exception
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		//loadFromMemory(clients);
		//数据库中查询，会自动去对应的表中查询
		clients.jdbc(dataSource);
	}

	/**
	 * 内存中配置客户端应用
	 * @param clients
	 * @throws Exception
	 */
	private void loadFromMemory(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.inMemory()
				//配置指定客户端访问指定的资源
				.withClient("orderApp")
				.secret(passwordEncoder.encode("123456"))
				//做acl的权限控制，此处即说明orderApp具有什么样的权限,orderApp可以获取到的所有权限的集合
				.scopes("read","write")
				.accessTokenValiditySeconds(3600)
				.refreshTokenValiditySeconds(3600)
				//resourceId代表资源服务器id,即发给orderApp的token可以访问那些资源服务器
				.resourceIds("order-server")
				//授权方式,oauth2中支持四种方式，这里表示orderApp可以使用哪种授权方式
				.authorizedGrantTypes("password")
				.and()
				.withClient("orderService")
				.secret(passwordEncoder.encode("123456"))
				//做acl的权限控制，此处即说明orderApp具有什么样的权限,orderApp可以获取到的所有权限的集合
				.scopes("read")
				.accessTokenValiditySeconds(3600)
				//resourceId代表资源服务器id,即发给orderApp的token可以访问那些资源服务器
				.resourceIds("order-server")
				//授权方式,oauth2中支持四种方式，这里表示orderApp可以使用哪
				// 种授权方式
				.authorizedGrantTypes("password");
	}

	/**
	 * 数据库存储-3，endpoints配置，使用jdbcTokenStore存取token
	 */
	/**
	 * 2.指定那些用户可以访问
	 * @param endpoints
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		//传入authenticationManager,它用来校验用户信息
		endpoints
				.tokenStore(tokenStore())
				.authenticationManager(authenticationManager);
	}

	/**
	 * 3.谁能找我验令牌
	 * @param security
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		//验token，必须是经过身份认证的，即带着用户名密码或者是orderApp/order-service,密码123456
		//来check token,如果直接发送一个请求，是不验证的
		security.checkTokenAccess("isAuthenticated()");
	}


}
